/*
 * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package master.flame.danmaku.danmaku.parser;

import android.text.TextUtils;

import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.Duration;
import master.flame.danmaku.danmaku.model.FBDanmaku;
import master.flame.danmaku.danmaku.model.FTDanmaku;
import master.flame.danmaku.danmaku.model.IDanmakuIterator;
import master.flame.danmaku.danmaku.model.IDanmakus;
import master.flame.danmaku.danmaku.model.IDisplayer;
import master.flame.danmaku.danmaku.model.L2RDanmaku;
import master.flame.danmaku.danmaku.model.R2LDanmaku;
import master.flame.danmaku.danmaku.model.SpecialDanmaku;
import master.flame.danmaku.danmaku.model.SpecialDanmaku.LinePath;
import master.flame.danmaku.danmaku.model.android.DanmakuGlobalConfig;
import master.flame.danmaku.danmaku.model.android.Danmakus;

public class DanmakuFactory {

    public final static float OLD_BILI_PLAYER_WIDTH = 539;
    
    public final static float BILI_PLAYER_WIDTH = 682;
    
    public static int CURRENT_DISP_WIDTH = 0, CURRENT_DISP_HEIGHT = 0;
    
    private static float CURRENT_DISP_SIZE_FACTOR = 1.0f;

    public final static float OLD_BILI_PLAYER_HEIGHT = 385;
    
    public final static float BILI_PLAYER_HEIGHT = 438;

    public final static long COMMON_DANMAKU_DURATION = 3800; // B站原始分辨率下弹幕存活时间
    
    public static final int DANMAKU_MEDIUM_TEXTSIZE = 25;

    public final static long MIN_DANMAKU_DURATION = 4000;
    
    public static long REAL_DANMAKU_DURATION = COMMON_DANMAKU_DURATION;

    public static long MAX_DANMAKU_DURATION = MIN_DANMAKU_DURATION;

    public final static long MAX_DANMAKU_DURATION_HIGH_DENSITY = 9000;
    
    public static Duration MAX_Duration_Scroll_Danmaku;
    
    public static Duration MAX_Duration_Fix_Danmaku;
    
    public static Duration MAX_Duration_Special_Danmaku;
    
    public static IDanmakus sSpecialDanmakus = new Danmakus();

    public static IDisplayer sLastDisp;
    
    public static void resetDurationsData() {
        sLastDisp = null;
        CURRENT_DISP_WIDTH = CURRENT_DISP_HEIGHT = 0;
        sSpecialDanmakus.clear();
        MAX_Duration_Scroll_Danmaku = null;
        MAX_Duration_Fix_Danmaku = null;
        MAX_Duration_Special_Danmaku = null;
        MAX_DANMAKU_DURATION = MIN_DANMAKU_DURATION;
    }
    
    public static void notifyDispSizeChanged(IDisplayer disp) {
        if (disp != null)
            sLastDisp = disp;
        createDanmaku(BaseDanmaku.TYPE_SCROLL_RL, disp);
    }
    
    public static BaseDanmaku createDanmaku(int type) {
        return createDanmaku(type, sLastDisp);
    }

    public static BaseDanmaku createDanmaku(int type, IDisplayer disp) {
        if (disp == null)
            return null;
        sLastDisp = disp;
        return createDanmaku(type, disp.getWidth(), disp.getHeight(), CURRENT_DISP_SIZE_FACTOR);
    }
    
    public static BaseDanmaku createDanmaku(int type, IDisplayer disp, float viewportScale) {
        if (disp == null)
            return null;
        sLastDisp = disp;
        return createDanmaku(type, disp.getWidth(), disp.getHeight(), viewportScale);
    } 
    
    /**
     * 创建弹幕数据请尽量使用此方法,参考BiliDanmakuParser或AcfunDanmakuParser
     * @param type 弹幕类型
     * @param viewportWidth danmakuview宽度,会影响滚动弹幕的存活时间(duration)
     * @param viewportHeight danmakuview高度
     * @param viewportScale 缩放比例,会影响滚动弹幕的存活时间(duration)
     * @return
     */
    public static BaseDanmaku createDanmaku(int type, int viewportWidth, int viewportHeight,
            float viewportScale) {
        return createDanmaku(type, (float) viewportWidth, (float) viewportHeight, viewportScale);
    }
    
    /**
     * 创建弹幕数据请尽量使用此方法,参考BiliDanmakuParser或AcfunDanmakuParser
     * @param type 弹幕类型
     * @param viewportWidth danmakuview宽度,会影响滚动弹幕的存活时间(duration)
     * @param viewportHeight danmakuview高度
     * @param viewportSizeFactor 会影响滚动弹幕的速度/存活时间(duration)
     * @return
     */
    public static BaseDanmaku createDanmaku(int type, float viewportWidth, float viewportHeight,
            float viewportSizeFactor) {
        boolean sizeChanged = updateViewportState(viewportWidth, viewportHeight, viewportSizeFactor);
        if (MAX_Duration_Scroll_Danmaku == null) {
            MAX_Duration_Scroll_Danmaku = new Duration(REAL_DANMAKU_DURATION);
            MAX_Duration_Scroll_Danmaku.setFactor(DanmakuGlobalConfig.DEFAULT.scrollSpeedFactor);
        } else if (sizeChanged) {
            MAX_Duration_Scroll_Danmaku.setValue(REAL_DANMAKU_DURATION);
        }
        
        if (MAX_Duration_Fix_Danmaku == null) {
            MAX_Duration_Fix_Danmaku = new Duration(COMMON_DANMAKU_DURATION);
        }

        if (sizeChanged && viewportWidth > 0) {
            updateMaxDanmakuDuration();
            float scaleX = 1f;
            float scaleY = 1f;
            if (CURRENT_DISP_WIDTH > 0 && CURRENT_DISP_HEIGHT > 0) {
                scaleX = viewportWidth / (float) CURRENT_DISP_WIDTH;
                scaleY = viewportHeight / (float) CURRENT_DISP_HEIGHT;
            }
            if (viewportHeight > 0) {
                updateSpecialDanmakusDate(scaleX, scaleY);
            }
        }

        BaseDanmaku instance = null;
        switch (type) {
            case 1: // 从右往左滚动
                instance = new R2LDanmaku(MAX_Duration_Scroll_Danmaku);
                break;
            case 4: // 底端固定
                instance = new FBDanmaku(MAX_Duration_Fix_Danmaku);
                break;
            case 5: // 顶端固定
                instance = new FTDanmaku(MAX_Duration_Fix_Danmaku);
                break;
            case 6: // 从左往右滚动
                instance = new L2RDanmaku(MAX_Duration_Scroll_Danmaku);
                break;
            case 7: // 特殊弹幕
                instance = new SpecialDanmaku();
                sSpecialDanmakus.addItem(instance);
                break;
        }
        return instance;
    }
    
    public static boolean updateViewportState(float viewportWidth, float viewportHeight,
            float viewportSizeFactor) {
        boolean sizeChanged = false;
        if (CURRENT_DISP_WIDTH != (int) viewportWidth
                || CURRENT_DISP_HEIGHT != (int) viewportHeight
                || CURRENT_DISP_SIZE_FACTOR != viewportSizeFactor) {
            sizeChanged = true;
            REAL_DANMAKU_DURATION = (long) (COMMON_DANMAKU_DURATION * (viewportSizeFactor
                    * viewportWidth / BILI_PLAYER_WIDTH));
            REAL_DANMAKU_DURATION = Math.min(MAX_DANMAKU_DURATION_HIGH_DENSITY,
                    REAL_DANMAKU_DURATION);
            REAL_DANMAKU_DURATION = Math.max(MIN_DANMAKU_DURATION, REAL_DANMAKU_DURATION);
            
            CURRENT_DISP_WIDTH = (int) viewportWidth;
            CURRENT_DISP_HEIGHT = (int) viewportHeight;
            CURRENT_DISP_SIZE_FACTOR = viewportSizeFactor;
        }
        return sizeChanged;
    }

    private static void updateSpecialDanmakusDate(float scaleX, float scaleY) {
        IDanmakus list = sSpecialDanmakus;
        IDanmakuIterator it = list.iterator();
        while (it.hasNext()) {
            SpecialDanmaku speicalDanmaku = (SpecialDanmaku) it.next();
            fillTranslationData(speicalDanmaku, speicalDanmaku.beginX, speicalDanmaku.beginY,
                    speicalDanmaku.endX, speicalDanmaku.endY, speicalDanmaku.translationDuration,
                    speicalDanmaku.translationStartDelay, scaleX, scaleY);
            LinePath[] linePaths = speicalDanmaku.linePaths;
            if (linePaths != null && linePaths.length > 0) {
                int length = linePaths.length;
                float[][] points = new float[length + 1][2];
                for (int j = 0; j < length; j++) {
                    points[j] = linePaths[j].getBeginPoint();
                    points[j + 1] = linePaths[j].getEndPoint();
                }
                fillLinePathData(speicalDanmaku, points, scaleX, scaleY);
            }
        }
    }

    public static void updateMaxDanmakuDuration() {
        long maxScrollDuration = (MAX_Duration_Scroll_Danmaku == null ? 0: MAX_Duration_Scroll_Danmaku.value), 
              maxFixDuration = (MAX_Duration_Fix_Danmaku == null ? 0 : MAX_Duration_Fix_Danmaku.value), 
              maxSpecialDuration = (MAX_Duration_Special_Danmaku == null ? 0: MAX_Duration_Special_Danmaku.value);

        MAX_DANMAKU_DURATION = Math.max(maxScrollDuration, maxFixDuration);
        MAX_DANMAKU_DURATION = Math.max(MAX_DANMAKU_DURATION, maxSpecialDuration);

        MAX_DANMAKU_DURATION = Math.max(COMMON_DANMAKU_DURATION, MAX_DANMAKU_DURATION);
        MAX_DANMAKU_DURATION = Math.max(REAL_DANMAKU_DURATION, MAX_DANMAKU_DURATION);
    }
    
    public static void updateDurationFactor(float f) {
        if (MAX_Duration_Scroll_Danmaku == null || MAX_Duration_Fix_Danmaku == null)
            return;
        MAX_Duration_Scroll_Danmaku.setFactor(f);
        updateMaxDanmakuDuration();
    }
    
    public static void fillText(BaseDanmaku danmaku, String text) {
        danmaku.text = text;
        if (TextUtils.isEmpty(text) || !text.contains(BaseDanmaku.DANMAKU_BR_CHAR)) {
            return;
        }
        
        String[] lines = danmaku.text.split(BaseDanmaku.DANMAKU_BR_CHAR, -1);
        if (lines.length > 1) {
            danmaku.lines = lines;
        }
    }

    /**
     * Initial translation data of the special danmaku
     * 
     * @param item
     * @param dispWidth
     * @param dispHeight
     * @param beginX
     * @param beginY
     * @param endX
     * @param endY
     * @param translationDuration
     * @param translationStartDelay
     */
    public static void fillTranslationData(BaseDanmaku item, float beginX, float beginY,
            float endX, float endY, long translationDuration, long translationStartDelay,
            float scaleX, float scaleY) {
        if (item.getType() != BaseDanmaku.TYPE_SPECIAL)
            return;
        ((SpecialDanmaku) item).setTranslationData(beginX * scaleX, beginY * scaleY, endX * scaleX,
                endY * scaleY, translationDuration, translationStartDelay);
        updateSpecicalDanmakuDuration(item);
    }
    
    public static void fillLinePathData(BaseDanmaku item, float[][] points, float scaleX,
            float scaleY) {
        if (item.getType() != BaseDanmaku.TYPE_SPECIAL || points.length == 0
                || points[0].length != 2)
            return;
        for (int i = 0; i < points.length; i++) {
            points[i][0] *= scaleX;
            points[i][1] *= scaleY;
        }
        ((SpecialDanmaku) item).setLinePathData(points);
    }

    /**
     * Initial alpha data of the special danmaku
     * 
     * @param item
     * @param beginAlpha
     * @param endAlpha
     * @param alphaDuraion
     */
    public static void fillAlphaData(BaseDanmaku item, int beginAlpha, int endAlpha,
            long alphaDuraion) {
        if (item.getType() != BaseDanmaku.TYPE_SPECIAL)
            return;
        ((SpecialDanmaku) item).setAlphaData(beginAlpha, endAlpha, alphaDuraion);
        updateSpecicalDanmakuDuration(item);
    }
    
    private static void updateSpecicalDanmakuDuration(BaseDanmaku item) {
        if (MAX_Duration_Special_Danmaku == null || (item.duration != null && item.duration.value > MAX_Duration_Special_Danmaku.value)) {
            MAX_Duration_Special_Danmaku = item.duration;
            updateMaxDanmakuDuration();
        }
    }
    
}
